#pragma once
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include "Nocopyable.h"

class Semaphore : private Nocopyable {
public:
    explicit Semaphore(int value = 0) {
        assert(sem_init(&m_Sem, 0, value) == 0);
    }

    ~Semaphore() {
        sem_destroy(&m_Sem);
    }

    void Post() {
        sem_post(&m_Sem);
    }

    void Wait() {
        sem_wait(&m_Sem);
    }

private:
    sem_t m_Sem;
};

class Mutex : private Nocopyable {
    friend class Condition;
public:
    Mutex() {
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        pthread_mutex_init(&m_Mutex, &attr);
        pthread_mutexattr_destroy(&attr);
    }

    ~Mutex() {
        pthread_mutex_destroy(&m_Mutex);
    }

    void Acquire() {
        pthread_mutex_lock(&m_Mutex);
    }

    void Release() {
        pthread_mutex_unlock(&m_Mutex);
    }

    bool TryAcquire() {
        return pthread_mutex_trylock(&m_Mutex) == 0;
    }

private:
    pthread_mutex_t m_Mutex;
};

class Condition : private Nocopyable {
public:
    Condition() {
        pthread_cond_init(&m_Cond, NULL);
    }

    ~Condition() {
        pthread_cond_destroy(&m_Cond);
    }

    void Wait(Mutex& mutex) {
        pthread_cond_wait(&m_Cond, &mutex.m_Mutex);
    }

    void Signal() {
        pthread_cond_signal(&m_Cond);
    }

    void Broadcast() {
        pthread_cond_broadcast(&m_Cond);
    }

private:
    pthread_cond_t m_Cond;
};

class ScopeMutex : private Nocopyable {
public:
    explicit ScopeMutex(Mutex* pMutex) : m_pMutex(pMutex) {
        m_pMutex->Acquire();
    }

    ~ScopeMutex() {
        m_pMutex->Release();
    }

private:
    Mutex* m_pMutex;
};
